package com.william.jdbcplus.core.query;

import com.william.jdbcplus.common.enums.DbType;
import com.william.jdbcplus.common.page.IPage;
import com.william.jdbcplus.common.page.Page;
import com.william.jdbcplus.core.conditions.lambda.Query;
import com.william.jdbcplus.core.conditions.segment.MergeSegments;
import com.william.jdbcplus.core.conditions.wrap.AbstractLambdaWrap;
import com.william.jdbcplus.core.dialects.pagination.PaginationFactory;
import com.william.jdbcplus.core.toolkit.MapTransformUtils;
import com.william.jdbcplus.core.util.JdbcUtil;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * lambda 通用查询封装
 *
 * @author niliwei
 */
public class QueryLambdaWrap extends AbstractLambdaWrap<QueryLambdaWrap> implements Query {

    public QueryLambdaWrap() {
        super.initNeed();
    }

    /**
     * 不建议直接 new 该实例，使用 Wrappers.lambdaQuery(...)
     */
    public QueryLambdaWrap(Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments) {
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
    }

    @Override
    protected QueryLambdaWrap instance() {
        return new QueryLambdaWrap(paramNameValuePairs, new MergeSegments());
    }

    @Override
    public List<Map<String, Object>> findMapList(DataSource ds) {
        NamedParameterJdbcTemplate jdbcOpt = getTemplate(ds);
        String sql = getAll();
        Map<String, Object> paramMap = this.getParamNameValuePairs();
        List<Map<String, Object>> listMap = jdbcOpt.queryForList(sql, paramMap);
        return listMap;
    }

    @Override
    public Map<String, Object> findMap(DataSource ds) {
        List<Map<String, Object>> list = findMapList(ds);
        return getFirst(list);
    }

    @Override
    public IPage<Map<String, Object>> findMapPage(IPage<?> page, DataSource ds) {
        NamedParameterJdbcTemplate jdbcOpt = getTemplate(ds);
        DbType dbType = JdbcUtil.getDbType(jdbcOpt.getJdbcTemplate().getDataSource());
        Integer totalCount = findCount(ds);
        List<Map<String, Object>> listMap = null;
        if (totalCount <= 0) {
            listMap = new ArrayList<>();
        } else {
            String sql = getAll();
            String sqlPage = PaginationFactory.getPaginationSql(page, sql, dbType);
            listMap = jdbcOpt.queryForList(sqlPage, this.getParamNameValuePairs());
        }

        Page<Map<String, Object>> resultPage = new Page<>(page.getCurrent(), page.getSize());
        resultPage.setRecords(listMap);
        resultPage.setTotal(totalCount);
        return resultPage;
    }

    @Override
    public Integer findCount(DataSource ds) {
        NamedParameterJdbcTemplate jdbcOpt = getTemplate(ds);
        String sqlCount = getCount();
        Map<String, Object> paramMap = this.getParamNameValuePairs();
        Integer count = jdbcOpt.queryForObject(sqlCount, paramMap, Integer.class);
        return count;
    }

    @Override
    public <M> M findOne(Class<M> clazz, DataSource ds) {
        return getFirst(findList(clazz, ds));
    }

    @Override
    public <M> List<M> findList(Class<M> clazz, DataSource ds) {
        List<Map<String, Object>> listMap = findMapList(ds);
        List<M> resultList = MapTransformUtils.mapToEntityList(listMap, clazz);
        return resultList;
    }

    @Override
    public <M> Page<M> findPage(IPage<?> page, Class<M> clazz, DataSource ds) {
        IPage<Map<String, Object>> mapPage = findMapPage(page, ds);
        List<Map<String, Object>> mapList = mapPage.getRecords();
        List<M> resultRows = MapTransformUtils.mapToEntityList(mapList, clazz);
        Page<M> pager = new Page<>(page.getCurrent(), page.getSize());
        pager.setTotal(mapPage.getTotal());
        pager.setRecords(resultRows);
        return pager;
    }

    private NamedParameterJdbcTemplate getTemplate(DataSource ds) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        return namedParameterJdbcTemplate;
    }
}
